
#include "stdafx.h"

#include "html_view_base.h"
#include "javascript_invoke.h"
#include "html_page.h"

// CHtmlViewBase

IMPLEMENT_DYNCREATE(CHtmlViewBase, CHtmlView)

BEGIN_DISPATCH_MAP(CHtmlViewBase, CHtmlView)
	DISP_FUNCTION(CHtmlViewBase, "debug", OnDebug, VT_EMPTY, VTS_BSTR)
	DISP_FUNCTION(CHtmlViewBase, "close", OnClose, VT_EMPTY, VTS_I4)
	DISP_FUNCTION(CHtmlViewBase, "loadString", OnLoadString, VT_BSTR, VTS_BSTR)
END_DISPATCH_MAP()



CHtmlViewBase::CHtmlViewBase()
:m_strRes( L"res" ), m_pHtmlPage( NULL )
{
}

CHtmlViewBase::~CHtmlViewBase()
{
}

void CHtmlViewBase::OnFinalRelease()
{
	// When the last reference for an automation object is released
	// OnFinalRelease is called.  The base class will automatically
	// deletes the object.  Add additional cleanup required for your
	// object before calling the base class.
	m_history.Clear();

	CHtmlView::OnFinalRelease();
}

void CHtmlViewBase::DoDataExchange(CDataExchange* pDX)
{
	CHtmlView::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CHtmlViewBase, CHtmlView)
END_MESSAGE_MAP()


// Note: we add support for IID_INotifyHtmlView to support typesafe binding
//  from VBA.  This IID must match the GUID that is attached to the 
//  dispinterface in the .IDL file.

// {F80EDE9A-80C3-4569-B2CD-9B5A9ABB551A}
static const IID IID_INotifyHtmlView =
{ 0xF80EDE9A, 0x80C3, 0x4569, { 0xB2, 0xCD, 0x9B, 0x5A, 0x9A, 0xBB, 0x55, 0x1A } };

BEGIN_INTERFACE_MAP(CHtmlViewBase, CHtmlView)
	INTERFACE_PART(CHtmlViewBase, IID_INotifyHtmlView, Dispatch)
END_INTERFACE_MAP()

BOOL CHtmlViewBase::Create( CRect const& rc, CWnd* pParent, UINT nID )
{
	EnableAutomation();
	BOOL bCreated = CHtmlView::Create(AfxRegisterWndClass(0), L"", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, rc, pParent, nID);
	if (bCreated)
	{
		OnCreate();
	}
	return bCreated;
}
// CHtmlViewBase diagnostics

#ifdef _DEBUG
void CHtmlViewBase::AssertValid() const
{
	CHtmlView::AssertValid();
}

void CHtmlViewBase::Dump(CDumpContext& dc) const
{
	CHtmlView::Dump(dc);
}
#endif //_DEBUG

void CHtmlViewBase::OnDocumentComplete( LPCTSTR lpszURL )
{
	// JavaScript: main();
	CallJavaScript(js_main());
	// C++ and HTML page initialize;
	this->OnReady( lpszURL );
}

HRESULT CHtmlViewBase::CallJavaScript(CString const& strJsCode, BOOL bSafe)
{
	CComPtr<IHTMLDocument2> pDoc;
	this->GetHtmlDocument()->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc);
	if (!pDoc)
	{
		return E_FAIL;
	}

	CComPtr<IHTMLWindow2> pWindow;
	if (FAILED(pDoc->get_parentWindow(&pWindow)))
	{
		return E_FAIL;
	}

	CComVariant bvResult;
	static CComBSTR javascript = L"javascript";

	CString strSafeJsCode;
	if (bSafe)
	{
		strSafeJsCode.Format(L"try{ %s } catch(e){ external.debug(e); }", strJsCode);
	}
	else
	{
		strSafeJsCode = strJsCode;
	}

	CComBSTR bstrSaveJsCode(strSafeJsCode);
	HRESULT hr = pWindow->execScript(bstrSaveJsCode, javascript, &bvResult);
	return hr;
}


void CHtmlViewBase::OnClose( int flag )
{

}

void CHtmlViewBase::CallBackJavaScript(CComPtr<IDispatch> const& pDisp, BOOL bValue)
{
	_com_dispatch_method( pDisp, 0, DISPATCH_METHOD, VT_EMPTY, NULL, L"\x0B", bValue );
}

void CHtmlViewBase::CallBackJavaScript(CComPtr<IDispatch> const& pDisp, LPCWSTR lpValue)
{
	_com_dispatch_method( pDisp, 0, DISPATCH_METHOD, VT_EMPTY, NULL, L"\x08", lpValue );
}

BOOL CHtmlViewBase::OpenPage( CString const& strPage )
{
	BOOL bRet = TRUE;
	CString strPath;
	WCHAR szPath[MAX_PATH] = {};
	GetModuleFileName( NULL, szPath, MAX_PATH );
#ifdef _DEBUG
	{
		ATLASSERT( !m_strRelative.IsEmpty() );
		PathAppend( szPath, L".." );
		PathAppend( szPath, m_strRelative );
		PathAppend( szPath, strPage );
		strPath = szPath;
	}
#else
	{
		if (!m_strRes.IsEmpty())
		{
			strPath.Format(_T("res://%s/%s/%s"), szPath, m_strRes, strPage);
		}
		else
		{
			strPath = L"about:blank";
			bRet = FALSE;
		}
	}
#endif
	Navigate2(strPath);
	return bRet;
}


BOOL CHtmlViewBase::OpenPage( CHtmlPage* pHtmlPage, BOOL bUseHistory )
{
	m_pHtmlPage = pHtmlPage;
	if (bUseHistory)
	{
		m_history.AddPage(pHtmlPage);
	}
	m_pHtmlPage->SetHtmlView( this );
	return OpenPage( pHtmlPage->GetPath() );
}

void CHtmlViewBase::SetResource( CString const& strRes )
{
	m_strRes = strRes;
}

void CHtmlViewBase::SetRelative( CString const& strRelative )
{
	m_strRelative = strRelative;
}
// CHtmlViewBase message handlers


BSTR CHtmlViewBase::OnLoadString( BSTR bstrUrl )
{
	// C++ Cache, it has a bigger life-Cycle than HTML pages;
	static map<CString, CString> s_str_cache;
	CString strUrl( bstrUrl );
	strUrl.MakeLower();

	// Not compile when not _DEBUG.
	// Good for debug HTML and other template strings.
#ifndef _DEBUG
	map<CString, CString>::iterator iter = s_str_cache.find( strUrl );
	if ( s_str_cache.end() != iter )
	{
		return iter->second.AllocSysString();
	}
#endif

	
	CString strRet;

	CString strPath;
	WCHAR szPath[MAX_PATH] = {};
	GetModuleFileName( NULL, szPath, MAX_PATH );
	
// TODO: Change the switch by compile time;
#ifdef _DEBUG
	{
		ATLASSERT( !m_strRelative.IsEmpty() );
		PathAppend( szPath, L".." );
		PathAppend( szPath, m_strRelative );
		PathAppend( szPath, bstrUrl );
		strPath = szPath;

		CFile file;
		if ( file.Open( strPath, CFile::modeRead ) )
		{
			DWORD dwSize = (DWORD)file.GetLength();
			
			unsigned char* p = new unsigned char[dwSize + 1];
			memset(p, 0, dwSize + 1);

			file.Read(p, dwSize);
			file.Close();

			strRet = CA2W( (char*)p, CP_UTF8 );
			delete[] p;

			s_str_cache[ strUrl ] = strRet;

		}

	}
#else
	{
		if (!m_strRes.IsEmpty())
		{
			strPath.Format(_T("res://%s/%s/%s"), szPath, m_strRes, bstrUrl);

			HRSRC hWebPageRes = FindResource(NULL, strPath, m_strRes);
			if (NULL != hWebPageRes)
			{
				DWORD dwSize = SizeofResource(NULL, hWebPageRes);
				HGLOBAL hGlobal = LoadResource(NULL, hWebPageRes);
				LPVOID pResContent = NULL;
				if(hGlobal != NULL)
				{
					pResContent = LockResource(hGlobal);
					strRet = CA2W(CStringA((LPSTR)pResContent, dwSize), CP_UTF8);

					s_str_cache[ strUrl ] = strRet;
					return strRet.AllocSysString();
				}
			}
		}
		else
		{
			strPath = L"about:blank";
			strRet = L"";
		}
	}
#endif
	return strRet.AllocSysString();
}

void CHtmlViewBase::OnReady( LPCTSTR lpszURL )
{
	GetCurrentPage()->OnReady( lpszURL );
}

CHtmlPage* CHtmlViewBase::GetCurrentPage() const
{
	ATLASSERT( NULL != m_pHtmlPage );
	return m_pHtmlPage;
}

void CHtmlViewBase::SetViewFocus()
{
	CComPtr<IDispatch> pDisp = GetHtmlDocument() ;
	if (pDisp)
	{
		CComPtr<IHTMLDocument2> pDoc;
		pDisp->QueryInterface (IID_IHTMLDocument2, (void**)&pDoc);
		if (pDoc)
		{
			CComPtr<IHTMLWindow2> pWnd;
			pDoc->get_parentWindow(&pWnd);
			if (pWnd)
			{
				pWnd->focus();
			}
		}
	}
}


LRESULT CHtmlViewBase::WindowProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	if ((msg == WM_WINDOWPOSCHANGED) || 
		(msg == WM_WINDOWPOSCHANGING) || 
		(msg == WM_ERASEBKGND))
	{
		if (IsWindowVisible())
		{
			LRESULT l = __super::WindowProc (msg, wParam, lParam);

			if (::IsWindow(m_wndBrowser))
			{
				m_wndBrowser.RedrawWindow (NULL, NULL, RDW_UPDATENOW | RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME);
			}
			return l;
		}
	}

	return __super::WindowProc(msg, wParam, lParam);
}